using System;
using System.Web;
using System.Security.Permissions;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using SharpPieces.Web.JQuery.Design;
using System.Drawing;

[assembly: WebResource("SharpPieces.Web.JQuery.TextBoxExtender.jquery.autogrow.js", "text/javascript")]
[assembly: WebResource("SharpPieces.Web.JQuery.TextBoxExtender.jquery.example.js", "text/javascript")]
[assembly: WebResource("SharpPieces.Web.JQuery.TextBoxExtender.jquery.example.min.js", "text/javascript")]
[assembly: WebResource("SharpPieces.Web.JQuery.TextBoxExtender.jquery.metadata.pack.js", "text/javascript")]

namespace SharpPieces.Web.JQuery
{

    /// <summary>
    /// Represents a TextBox extender containing various plugins.
    /// </summary>
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [Designer(typeof(TextBoxExtenderDesigner)), Description("TextBox extender containing various plugins."), DefaultProperty("TargetControlID")]
    [ToolboxData("<{0}:TextBoxExtender runat=\"server\"></{0}:TextBoxExtender>"), PersistChildren(false), ParseChildren(true), Themeable(true)]
    [ToolboxBitmap(typeof(TextBoxExtender), "TextBoxExtender.TextBoxExtender.bmp")]
    public class TextBoxExtender : BaseJQueryExtender
    {

        // Methods

        /// <summary>
        /// Gets the JQuery object properties.
        /// The key is the object property name.
        /// The value is the object property initial value.
        /// </summary>
        protected override IEnumerable<KeyValuePair<string, object>> GetJQueryObjectProperties()
        {
            yield break;
        }

        /// <summary>
        /// Gets the JQuery object events.
        /// The key is the object event name.
        /// The value is the object event handler.
        /// </summary>
        protected override IEnumerable<KeyValuePair<string, string>> GetJQueryObjectEvents()
        {
            yield break;
        }

        /// <summary>
        /// Gets the JQuery script references.
        /// </summary>
        protected override IEnumerable<ScriptReference> GetJQueryScriptReferences()
        {
#if DEBUG
            if (this.FormInputExample.Enabled)
            {
                yield return new ScriptReference(
                    this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "SharpPieces.Web.JQuery.TextBoxExtender.jquery.example.js"),
                    typeof(TextBoxExtender).Assembly.FullName);
            }

            if (this.AutoGrow.Enabled)
            {
                yield return new ScriptReference(
                    this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "SharpPieces.Web.JQuery.TextBoxExtender.jquery.autogrow.js"),
                    typeof(TextBoxExtender).Assembly.FullName);
            }
#else
            if (this.FormInputExample.Enabled)
            {
                yield return new ScriptReference(
                    this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "SharpPieces.Web.JQuery.TextBoxExtender.jquery.example.min.js"), 
                    typeof(TextBoxExtender).Assembly.FullName);
            }

            if (this.AutoGrow.Enabled)
            {
                yield return new ScriptReference(
                    this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "SharpPieces.Web.JQuery.TextBoxExtender.jquery.autogrow.js"),
                    typeof(TextBoxExtender).Assembly.FullName);
            }
#endif
        }

        /// <summary>
        /// Gets the client initialization scripts.
        /// The key is the script name and the value is the script.
        /// </summary>
        /// <value>The client initialization scripts.</value>
        protected override IEnumerable<KeyValuePair<string, string>> GetClientScriptInitializations()
        {
            TextBox target = this.ResolveTargetControl<TextBox>();

            if (this.FormInputExample.Enabled)
            {
                Dictionary<string, string> options = new Dictionary<string, string>();

                // css class JQuery Form Input Example
                if (!string.IsNullOrEmpty(this.FormInputExample.CssClass))
                {
                    options["class_name"] = string.Format("'{0}'", this.FormInputExample.CssClass);
                }

                // hide label JQuery Form Input Example
                if (this.FormInputExample.HideLabel)
                {
                    options["hide_label"] = "true";
                }

                yield return new KeyValuePair<string, string>(
                    string.Format("{0}InitTextBoxExtender", this.TargetControlID),
                    string.Format(
                        "<script type=\"text/javascript\">jQuery('#{0}').example('{1}', {2});</script>",
                        target.ClientID,
                        this.FormInputExample.Text,
                        BaseJQueryExtender.OptionListToJS(options))
                    );
            }

            if ((TextBoxMode.MultiLine == target.TextMode) && this.AutoGrow.Enabled)
            {
                // todo custom properties + line-height as property

                yield return new KeyValuePair<string, string>(
                    string.Format("{0}InitAutoGrow", this.TargetControlID),
                    string.Format(
                        "<script type=\"text/javascript\">jQuery('#{0}').css('line-height', '18px').autogrow();</script>",
                        target.ClientID)
                );
            }

            yield break;
        }

        /// <summary>
        /// Gets the control style references.
        /// </summary>
        /// <returns></returns>
        /// <value>The control style references.</value>
        protected override IEnumerable<string> GetClientStyleReferences()
        {
            yield break;
        }

        // Properties

        /// <summary>
        /// Gets or sets the target control ID.
        /// </summary>
        /// <value>The target control ID.</value>
        [TypeConverter(typeof(TextBoxControlConverter))]
        public override string TargetControlID
        {
            get { return base.TargetControlID; }
            set { base.TargetControlID = value; }
        }

        /// <summary>
        /// Gets or sets the configuration for the FormInputExample plugin (Paul Mucur).
        /// </summary>
        /// <value>The configuration for the FormInputExample plugin (Paul Mucur).</value>
        [Description("The configuration for the FormInputExample plugin (Paul Mucur)."), Category("Plugins"), DefaultValue((string)null), MergableProperty(false), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.InnerProperty), Themeable(false)]
        public virtual FormInputExample FormInputExample
        {
            get
            {
                if (null == this.ViewState["FormInputExample"])
                {
                    FormInputExample fie = new FormInputExample();
                    if (this.EnableViewState)
                    {
                        fie.TrackViewState();
                    }
                    this.ViewState["FormInputExample"] = fie;
                }
                return (FormInputExample)this.ViewState["FormInputExample"];
            }
            set
            {
                if ((null != value) && this.EnableViewState)
                {
                    value.TrackViewState();
                }
                this.ViewState["FormInputExample"] = value;
            }
        }

        /// <summary>
        /// Gets or sets the configuration for the AutoGrow plugin (Chrys Bader).
        /// </summary>
        /// <value>The configuration for the AutoGrow plugin (Chrys Bader).</value>
        [Description("The configuration for the AutoGrow plugin (Chrys Bader)."), Category("Plugins"), DefaultValue((string)null), MergableProperty(false), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.InnerProperty), Themeable(false)]
        public virtual AutoGrow AutoGrow
        {
            get
            {
                if (null == this.ViewState["AutoGrow"])
                {
                    AutoGrow ag = new AutoGrow();
                    if (this.EnableViewState)
                    {
                        ag.TrackViewState();
                    }
                    this.ViewState["AutoGrow"] = ag;
                }
                return (AutoGrow)this.ViewState["AutoGrow"];
            }
            set
            {
                if ((null != value) && this.EnableViewState)
                {
                    value.TrackViewState();
                }
                this.ViewState["AutoGrow"] = value;
            }
        }
        
    }


    /// <summary>
    /// Represents a FormInputExample plugin implementation.
    /// </summary>
    [Serializable]
    [TypeConverter(typeof(ExpandableObjectConverter)), DefaultProperty("Enabled")]
    public class FormInputExample : IStateManager
    {

        // Fields

        private bool tracks;
        private DirtyFlags dirtyFlags = DirtyFlags.None;
        private bool enabled = false;
        private string text = null;
        private string cssClass = null;
        private bool hideLabel = false;


        // Methods

        /// <summary>
        /// When implemented by a class, gets a value indicating whether a server control is tracking its view state changes.
        /// </summary>
        /// <value></value>
        /// <returns>true if a server control is tracking its view state changes; otherwise, false.</returns>
        internal bool IsTrackingViewState
        {
            get { return this.tracks; }
        }

        /// <summary>
        /// When implemented by a class, loads the server control's previously saved view state to the control.
        /// </summary>
        /// <param name="state">An <see cref="T:System.Object"></see> that contains the saved view state values for the control.</param>
        internal virtual void LoadViewState(object state)
        {
            if (null != state)
            {
                object[] s = state as object[];
                if (null != s[0])
                {
                    this.Enabled = (bool)s[0];
                }
                if (null != s[1])
                {
                    this.Text = (string)s[1];
                }
                if (null != s[2])
                {
                    this.CssClass = (string)s[2];
                }
                if (null != s[3])
                {
                    this.HideLabel = (bool)s[3];
                }
            }
        }

        /// <summary>
        /// When implemented by a class, saves the changes to a server control's view state to an <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Object"></see> that contains the view state changes.
        /// </returns>
        internal virtual object SaveViewState()
        {
            object[] state = new object[4];

            state[0] = ((this.dirtyFlags & DirtyFlags.Enabled) == DirtyFlags.Enabled) ? (object)this.Enabled : null;
            state[1] = ((this.dirtyFlags & DirtyFlags.Text) == DirtyFlags.Text) ? (object)this.Text : (object)null;
            state[2] = ((this.dirtyFlags & DirtyFlags.CssClass) == DirtyFlags.CssClass) ? (object)this.CssClass : (object)null;
            state[3] = ((this.dirtyFlags & DirtyFlags.HideLabel) == DirtyFlags.HideLabel) ? (object)this.HideLabel : (object)null;

            return state;
        }

        /// <summary>
        /// When implemented by a class, instructs the server control to track changes to its view state.
        /// </summary>
        internal void TrackViewState()
        {
            this.tracks = true;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            return string.Empty;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if ((null == obj) || (typeof(FormInputExample) != obj.GetType()))
            {
                return false;
            }

            FormInputExample compared = (FormInputExample)obj;
            return
                (this.Enabled == compared.Enabled) &&
                (this.Text == compared.Text) &&
                (this.CssClass == compared.CssClass) &&
                (this.HideLabel == compared.HideLabel);
        }

        /// <summary>
        /// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is suitable for use in hashing algorithms and data structures like a hash table.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }


        // Properties

        /// <summary>
        /// Gets or sets a value indicating whether to enable this plugin.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this plugin should be enabled; otherwise, <c>false</c>.
        /// </value>
        [Description("Indicates whether to enable this plugin."), DefaultValue(false), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.Attribute), Themeable(true)]
        public virtual bool Enabled
        {
            get { return this.enabled; }
            set
            {
                this.enabled = value;
                if ((this as IStateManager).IsTrackingViewState)
                {
                    this.dirtyFlags = this.dirtyFlags | DirtyFlags.Enabled;
                }
            }
        }

        /// <summary>
        /// Gets or sets the text.
        /// </summary>
        /// <value>The text.</value>
        [Description("The text."), DefaultValue(""), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.Attribute), Themeable(false)]
        public virtual string Text
        {
            get { return this.text; }
            set
            {
                this.text = value;
                if ((this as IStateManager).IsTrackingViewState)
                {
                    this.dirtyFlags = this.dirtyFlags | DirtyFlags.Text;
                }
            }
        }

        /// <summary>
        /// Gets or sets the css class.
        /// </summary>
        /// <value>The css class.</value>
        [Description("The css class."), DefaultValue(""), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.Attribute), Themeable(true)]
        public virtual string CssClass
        {
            get { return this.cssClass; }
            set
            {
                this.cssClass = value;
                if ((this as IStateManager).IsTrackingViewState)
                {
                    this.dirtyFlags = this.dirtyFlags | DirtyFlags.CssClass;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to hide any adjacent label or line-break.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the JQuery Form Input Example should hide any adjacent label or line-break; otherwise, <c>false</c>.
        /// </value>
        [Description("Indicating whether to hide any adjacent label or line-break."), DefaultValue(false), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.Attribute), Themeable(false)]
        public virtual bool HideLabel
        {
            get { return this.hideLabel; }
            set
            {
                this.hideLabel = value;
                if ((this as IStateManager).IsTrackingViewState)
                {
                    this.dirtyFlags = this.dirtyFlags | DirtyFlags.HideLabel;
                }
            }
        }

        // Nested Types

        private enum DirtyFlags
        {
            None = 0,
            Enabled = 1,
            Text = 2,
            CssClass = 4,
            HideLabel = 8,
        }

        #region IStateManager Members

        /// <summary>
        /// When implemented by a class, gets a value indicating whether a server control is tracking its view state changes.
        /// </summary>
        /// <value></value>
        /// <returns>true if a server control is tracking its view state changes; otherwise, false.</returns>
        bool IStateManager.IsTrackingViewState
        {
            get { return this.IsTrackingViewState; }
        }

        /// <summary>
        /// When implemented by a class, loads the server control's previously saved view state to the control.
        /// </summary>
        /// <param name="state">An <see cref="T:System.Object"></see> that contains the saved view state values for the control.</param>
        void IStateManager.LoadViewState(object state)
        {
            this.LoadViewState(state);
        }

        /// <summary>
        /// When implemented by a class, saves the changes to a server control's view state to an <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Object"></see> that contains the view state changes.
        /// </returns>
        object IStateManager.SaveViewState()
        {
            return this.SaveViewState();
        }

        /// <summary>
        /// When implemented by a class, instructs the server control to track changes to its view state.
        /// </summary>
        void IStateManager.TrackViewState()
        {
            this.TrackViewState();
        }

        #endregion

    }


    /// <summary>
    /// Represents an AutoGrow plugin implementation.
    /// </summary>
    [Serializable]
    [TypeConverter(typeof(ExpandableObjectConverter)), DefaultProperty("Enabled")]
    public class AutoGrow : IStateManager
    {

        // Fields

        private bool tracks;
        private DirtyFlags dirtyFlags = DirtyFlags.None;
        private bool enabled = false;


        // Methods

        /// <summary>
        /// When implemented by a class, gets a value indicating whether a server control is tracking its view state changes.
        /// </summary>
        /// <value></value>
        /// <returns>true if a server control is tracking its view state changes; otherwise, false.</returns>
        internal bool IsTrackingViewState
        {
            get { return this.tracks; }
        }

        /// <summary>
        /// When implemented by a class, loads the server control's previously saved view state to the control.
        /// </summary>
        /// <param name="state">An <see cref="T:System.Object"></see> that contains the saved view state values for the control.</param>
        internal virtual void LoadViewState(object state)
        {
            if (null != state)
            {
                object[] s = state as object[];
                if (null != s[0])
                {
                    this.Enabled = (bool)s[0];
                }
            }
        }

        /// <summary>
        /// When implemented by a class, saves the changes to a server control's view state to an <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Object"></see> that contains the view state changes.
        /// </returns>
        internal virtual object SaveViewState()
        {
            object[] state = new object[4];

            state[0] = ((this.dirtyFlags & DirtyFlags.Enabled) == DirtyFlags.Enabled) ? (object)this.Enabled : null;

            return state;
        }

        /// <summary>
        /// When implemented by a class, instructs the server control to track changes to its view state.
        /// </summary>
        internal void TrackViewState()
        {
            this.tracks = true;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            return string.Empty;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if ((null == obj) || (typeof(AutoGrow) != obj.GetType()))
            {
                return false;
            }

            AutoGrow compared = (AutoGrow)obj;
            return (this.Enabled == compared.Enabled);
        }

        /// <summary>
        /// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is suitable for use in hashing algorithms and data structures like a hash table.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }


        // Properties

        /// <summary>
        /// Gets or sets a value indicating whether to enable this plugin.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this plugin should be enabled; otherwise, <c>false</c>.
        /// </value>
        [Description("Indicates whether to enable this plugin."), DefaultValue(false), NotifyParentProperty(true)]
        [PersistenceMode(PersistenceMode.Attribute), Themeable(true)]
        public virtual bool Enabled
        {
            get { return this.enabled; }
            set
            {
                this.enabled = value;
                if ((this as IStateManager).IsTrackingViewState)
                {
                    this.dirtyFlags = this.dirtyFlags | DirtyFlags.Enabled;
                }
            }
        }


        // Nested Types

        private enum DirtyFlags
        {
            None = 0,
            Enabled = 1,
        }

        #region IStateManager Members

        /// <summary>
        /// When implemented by a class, gets a value indicating whether a server control is tracking its view state changes.
        /// </summary>
        /// <value></value>
        /// <returns>true if a server control is tracking its view state changes; otherwise, false.</returns>
        bool IStateManager.IsTrackingViewState
        {
            get { return this.IsTrackingViewState; }
        }

        /// <summary>
        /// When implemented by a class, loads the server control's previously saved view state to the control.
        /// </summary>
        /// <param name="state">An <see cref="T:System.Object"></see> that contains the saved view state values for the control.</param>
        void IStateManager.LoadViewState(object state)
        {
            this.LoadViewState(state);
        }

        /// <summary>
        /// When implemented by a class, saves the changes to a server control's view state to an <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// The <see cref="T:System.Object"></see> that contains the view state changes.
        /// </returns>
        object IStateManager.SaveViewState()
        {
            return this.SaveViewState();
        }

        /// <summary>
        /// When implemented by a class, instructs the server control to track changes to its view state.
        /// </summary>
        void IStateManager.TrackViewState()
        {
            this.TrackViewState();
        }

        #endregion
    }


    /// <summary>
    /// Provides a type converter that retrieves a list of control IDs in the current 
    /// container, of the TextBox type or convertible.
    /// </summary>
    internal class TextBoxControlConverter : ControlIDConverter
    {

        // Methods

        /// <summary>
        /// Returns a value indicating whether the control ID of the specified control is added to the <see cref="T:System.ComponentModel.TypeConverter.StandardValuesCollection"></see> that is returned by the <see cref="M:System.Web.UI.WebControls.ControlIDConverter.GetStandardValues(System.ComponentModel.ITypeDescriptorContext)"></see> method.
        /// </summary>
        /// <param name="control">The control instance to test for inclusion in the <see cref="T:System.ComponentModel.TypeConverter.StandardValuesCollection"></see>.</param>
        /// <returns>true in all cases.</returns>
        protected override bool FilterControl(Control control)
        {
            return typeof(TextBox).IsInstanceOfType(control);
        }

    }

}
